package calculator;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;

import exceptions.InvalidExpressionException;

/*
 * EquationEvaluator.java
 * Author: Ben McCormick
 * Written: Jan 2 2011
 * Last Edited: May 16 2011
 * Ben McCormick 2011
 * This file is part of The Eikona Project .
 *    Copyright 2011 Ben McCormick
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *    
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 */

/**
 * A class to evaluate mathematical expressions
 * @author Ben McCormick
 */
public class EquationEvaluator implements EikonaCalculator{

	EquationTokenizer et;
	EquationScanner es;
	EquationTreeBuilder etb;
	VariableMap vm;
	private boolean radians = true;
	
	/**
	 * A plain constructor, instantiates a new EquationEvaluator
	 */
	public EquationEvaluator()
	{
		et = EquationTokenizer.getInstance();
		vm=VariableMap.getInstance();
	}	
	
	/**
	 * Constructor with an expression
	 * @param expression
	 * @throws Exception
	 */
	public EquationEvaluator(String expression) throws Exception
	{
		vm=VariableMap.getInstance();
		et = EquationTokenizer.getInstance();
		es = new EquationScanner(et.tokenize("#"+expression+"#"),vm);
		etb = new EquationTreeBuilder(es);
	}
	
	/**
	 * Adds a variable to the evaluator
	 */
	public void addVariable(String v, BigDecimal val) 
	{
		vm.addVariable(v, val, 0);
	}
	
	/** 
	 * Gets the angle Units (Radians or Degrees)
	 */
	public int getAngleUnits() 
	{
		if(radians)
		return IEvaluator.RADIANS;
		else
			return IEvaluator.DEGREES;
	}
	
	/** 
	 * sets the angle units (gradians not currently supported )
	 */
	public void setAngleUnits(int units) 
	{
		if(etb==null)
		{
			try{
			es = new EquationScanner(new String[0],vm);
			etb = new EquationTreeBuilder(es);
			}
			catch(Exception e)
			{
				e.printStackTrace();
				System.exit(0);
			}
		}
		if(units == IEvaluator.RADIANS || units == IEvaluator.GRADIANS)
		{
			etb.setRadians(true);
			radians = true;
		}
		else
		{
			etb.setRadians(false);
			radians = false;
		}
	}
	
	/**
	 * Returns the value of the variable
	 */
	public BigDecimal getVariable(String varname) 
	{
		return vm.getVarValue(varname);
	}

	/** 
	 * Sets the Evaluator to evaluate a new expression 
	 * @throws IOException 
	 * */
	public BigDecimal compute(String expression) throws InvalidExpressionException, NumberFormatException, InvalidExpressionException, IOException
	{
		if(es == null){es = new EquationScanner(et.tokenize("#"+expression+"#"),vm);}
		else{es.newExpression(et.tokenize("#"+expression+"#"));}
		etb = new EquationTreeBuilder(es);
		etb.setRadians(radians);
		return etb.getValue();
	}
}
